﻿#include <windows.h>
#include <cstdio>
#include <hge.h>
#include "../config.h"
#include "../utility.h"
#include "map.h"
#include "npc.h"
#include "player.h"
#include "input.h"

enum GameState { STATE_MAIN, STATE_MAP_SWITCH, STATE_DIALOG, STATE_MENU };

GameState eState = STATE_MAIN;

// Game Objects
HGE *pHGE = NULL;
Map kMap;
Image kImageTiles[SHEET_COUNT], kImageTools;
Image kImageDigits;
Image kImageEffects;
Image kImageHeads;
Input kInput;
Font kFont;

// Global Variables
bool g_bDebug = false;

int g_iSwitchProgress = 0;
int g_iSwitchPlayerX, g_iSwitchPlayerY;
int g_iSwitchMapId;

char const *g_sDialog = NULL;
int g_sDialogHead = 0;

char const *g_sMenuItem[] = {"状态", "道具", "装备", "魔法", "系统"};
int g_iMenuItem = 0;

Player	kPlayer;
NPC		kNPC;


int iViewX = 0;	// 摄像机左上角在地图上的位置
int iViewY = 0;	// 摄像机左上角在地图上的位置
int GetViewX() { return iViewX; }
int GetViewY() { return iViewY; }
void CenterView(Character const& character, Map const& map)
{
	if (CX_VIEW / 2 > character.m_x) { iViewX = 0; }
	else if (character.m_x > map.GetWidth() - CX_VIEW / 2) { iViewX = map.GetWidth() - CX_VIEW; }
	else { iViewX = character.m_x - CX_VIEW / 2; }

	if (CY_VIEW / 2 > character.m_y) { iViewY = 0; }
	else if (character.m_y > map.GetHeight() - CY_VIEW / 2) { iViewY = map.GetHeight() - CY_VIEW; }
	else { iViewY = character.m_y - CY_VIEW / 2; }
}

bool LoadRes()
{
	pHGE->System_Log("Loading Tiles...");
	for (int i = 0; i < SHEET_COUNT; i++) {
		if (!kImageTiles[i].Load(sSheetFileName[i]))
			return false;
		kImageTiles[i].SetFrameInfo(Map::CX_TILE, Map::CY_TILE, X_TILE_COUNT);
	}

	pHGE->System_Log("Loading Character Image...");
	if (!kPlayer.LoadRes())	return false;
	if (!kNPC.LoadRes())	return false;

	pHGE->System_Log("Loading Debug Image...");
	if (!kImageTools.Load(TOOLS_FILENAME))
		return false;
	kImageTools.SetFrameInfo(Map::CX_TILE, Map::CY_TILE, 4);

	if (!kImageDigits.Load(DIGITS_FILENAME))
		return false;
	kImageDigits.SetFrameInfo(9, 14, 7);

	pHGE->System_Log("Loading Map...");
	if (!kMap.Load(sMapFileName[0]))
		return false;
	kMap.GetTile(kPlayer.m_x / Map::CX_TILE, kPlayer.m_y / Map::CY_TILE).bBlocked = true;
	kMap.GetTile(kNPC.m_x / Map::CX_TILE, kNPC.m_y / Map::CY_TILE).bBlocked = true;

	pHGE->System_Log("Loading Effect Image...");
	if (!kImageEffects.Load(EFFECTS_FILENAME))
		return false;
	kImageEffects.SetFrameInfo(Map::CX_TILE, Map::CY_TILE, 1);

	pHGE->System_Log("Loading Font...");
	if (!kFont.Create("文泉驿等宽微米黑", 23))
		return false;

	pHGE->System_Log("Loading Head Images...");
	if (!kImageHeads.Load(HEADS_FILENAME))
		return false;

	return true;
}

void FreeRes()
{
	for (int i = 0; i < SHEET_COUNT; i++) {
		kImageTiles[i].Free();
	}
	kImageTools.Free();
	kImageDigits.Free();
	kPlayer.FreeRes();
	kNPC.FreeRes();
	kMap.Free();
	kImageEffects.Free();
	kImageHeads.Free();
	kFont.Free();
}

void SwitchMap(int map, int x, int y)
{
	g_iSwitchProgress = 0;
	g_iSwitchMapId	 = map;
	g_iSwitchPlayerX = x;
	g_iSwitchPlayerY = y;

	eState = STATE_MAP_SWITCH;
}

void Dialog(int head, char const *text)
{
	kInput.up = kInput.down = kInput.left = kInput.right = 0;

	g_sDialogHead = head;
	g_sDialog = text;
	eState = STATE_DIALOG;
}

void RemoveTrap(int x, int y, int w, int h)
{
	for (int m = y; m < y + h; m++) {
		for (int n = x; n < x + w; n++) {
			kMap.GetTile(n, m).iTrapId = Map::BLANK_TRAP;
		}
	}
}

void Trap(Player const& player, Map const& map)
{
	unsigned char const trapId = map.GetTile(player.m_x / Map::CX_TILE, player.m_y / Map::CY_TILE).iTrapId;

	if (trapId != Map::BLANK_TRAP) {
		switch (trapId) {
			case 1:		SwitchMap(1, 35, 33);	break;
			case 6:		SwitchMap(0, 10,  1);	break;
			case 7:		SwitchMap(2,  9, 13);	break;
			case 11:	SwitchMap(1,  4,  1);	break;
			case 12:	Dialog(-1, "传说中的剧情触发点……");	RemoveTrap(19, 13, 1, 5);	break;
			case 3:		Dialog(0, "【主角】\n别人家还是不要进去的好……");	kInput.up = 0; kInput.down = 1; break;
		}
	}
}

void UpdateMain(Input& kInput)
{
	if (kPlayer.m_x % Map::CX_TILE == 0 && kPlayer.m_y % Map::CY_TILE == 0) {

		Trap(kPlayer, kMap);

		if (kInput.action == 1) {
			if (kNPC.IsInFrontOf(kPlayer)) {
				kNPC.FaceTo(kPlayer);
				Dialog(0, "【？？？】\n 你是谁？这是哪里？我为什么会在这里？现在几点了？\n 还有……我为什么只会说这句话？");
            } else {
                eState = STATE_MENU;
            }
		}

		kPlayer.HandleInput(kInput, kMap);
	}

	kPlayer.Update(kMap);

	CenterView(kPlayer, kMap);
}

void RenderMain()
{
	int const iMapGridX = GetViewX() / Map::CX_TILE;
	int const iMapGridY = GetViewY() / Map::CY_TILE;

	for (int y = iMapGridY; y <= iMapGridY + Y_GRID_COUNT && y < kMap.GetYCount(); y++) {
		for (int x = iMapGridX; x <= iMapGridX + X_GRID_COUNT && x < kMap.GetXCount(); x++) {
			Map::Tile const& tile = kMap.GetTile(x, y);
			float const tileX = static_cast<float>(x * Map::CX_TILE - GetViewX());
			float const tileY = static_cast<float>(y * Map::CY_TILE - GetViewY());

			if (tile.iGroundId != Map::BLANK_TILE) {
				kImageTiles[tile.iGroundId / TILES_PER_SHEET].SetFrame(tile.iGroundId % TILES_PER_SHEET);
				kImageTiles[tile.iGroundId / TILES_PER_SHEET].Render(tileX, tileY);
			}
		}

		if ((y - 1) * Map::CY_TILE < kPlayer.m_y && kPlayer.m_y <= y * Map::CY_TILE)
			kPlayer.Render(GetViewX(), GetViewY());

		if ((y - 1) * Map::CY_TILE < kNPC.m_y && kNPC.m_y <= y * Map::CY_TILE)
			kNPC.Render(GetViewX(), GetViewY());
	}

	for (int y = iMapGridY; y <= iMapGridY + Y_GRID_COUNT && y < kMap.GetYCount(); y++) {
		for (int x = iMapGridX; x <= iMapGridX + X_GRID_COUNT && x < kMap.GetXCount(); x++) {
			Map::Tile const& tile = kMap.GetTile(x, y);
			float const tileX = static_cast<float>(x * Map::CX_TILE - GetViewX());
			float const tileY = static_cast<float>(y * Map::CY_TILE - GetViewY());

			if (tile.iSkyId != Map::BLANK_TILE) {
				kImageTiles[tile.iSkyId / TILES_PER_SHEET].SetFrame(tile.iSkyId % TILES_PER_SHEET);
				kImageTiles[tile.iSkyId / TILES_PER_SHEET].Render(tileX, tileY);
			}

			if (g_bDebug && tile.iTrapId != Map::BLANK_TRAP) {
				kImageTools.SetFrame(3);
				kImageTools.Render(tileX, tileY);
				kImageDigits.RenderNumber(tileX + 3, tileY, tile.iTrapId);
			}

			if (g_bDebug && tile.bBlocked) {
				kImageTools.SetFrame(1);
				kImageTools.Render(tileX, tileY);
			}
		}
	}

	if (g_bDebug) {
		static char sBuffer[256];
		sprintf(sBuffer, "X:%d Y:%d", kPlayer.m_x, kPlayer.m_y);
		kFont.Render(0, 0, sBuffer, 0xFFFF00FF, true);
	}
}

void UpdateMapSwitch()
{
	g_iSwitchProgress += 5;

	if (g_iSwitchProgress == 255) {
		kMap.Load(sMapFileName[g_iSwitchMapId]);

		kPlayer.m_x = g_iSwitchPlayerX * Map::CX_TILE;
		kPlayer.m_y = g_iSwitchPlayerY * Map::CY_TILE;

		kMap.GetTile(kPlayer.m_x / Map::CX_TILE, kPlayer.m_y / Map::CY_TILE).bBlocked = true;
		kMap.GetTile(kNPC.m_x / Map::CX_TILE, kNPC.m_y / Map::CY_TILE).bBlocked = true;

		CenterView(kPlayer, kMap);
	} else if (g_iSwitchProgress > 512) {
		eState = STATE_MAIN;
	}
}

void RenderMapSwitch()
{
	RenderMain();

	kImageEffects.SetAlpha(g_iSwitchProgress > 255 ? 512 - g_iSwitchProgress : g_iSwitchProgress);
	kImageEffects.SetFrame(0);
	kImageEffects.Render(0, 0, 0, X_GRID_COUNT, Y_GRID_COUNT);
}

void UpdateMenu(Input const& kInput)
{
    if (kInput.action == 1) {
        eState = STATE_MAIN;
    }
    if (kInput.up   % 10 == 1) g_iMenuItem = (g_iMenuItem - 1) % (sizeof(g_sMenuItem) / sizeof(g_sMenuItem[0]));
    if (kInput.down % 10 == 1) g_iMenuItem = (g_iMenuItem + 1) % (sizeof(g_sMenuItem) / sizeof(g_sMenuItem[0]));
}

void RenderMenu()
{
    RenderMain();

    kImageEffects.SetAlpha(0x99);
    kImageEffects.SetFrame(0);
    kImageEffects.Render(0, 0, 0, X_GRID_COUNT, Y_GRID_COUNT);

    kFont.Render(100, 60, "菜单", 0xFFCCCCCC);
    
    for (int i = 0; i < sizeof(g_sMenuItem) / sizeof(g_sMenuItem[0]); i++) {
        int xOffset = (i == g_iMenuItem) ? 5 : 0;
        DWORD color = (i == g_iMenuItem) ? 0xFFFF0000 : 0xFFFFFFFF;
        kFont.Render(100 + xOffset, 100 + 30 * i, g_sMenuItem[i], color);
    }
}

void UpdateDialog(Input const& kInput)
{
	if (kInput.action == 1) {
		eState = STATE_MAIN;
	}
}

void RenderDialog()
{
	RenderMain();

	kImageEffects.SetAlpha(0x99);
	kImageEffects.SetFrame(0);
	kImageEffects.Render(0, 352, 0, X_GRID_COUNT, 3);

	if (g_sDialogHead != -1) {
		kImageHeads.SetFrame(g_sDialogHead);
		kImageHeads.Render(0, 96);
	}

	kFont.Render(10, 365, g_sDialog, 0xFFFFFFFF, true);
}

bool FrameFunc()
{	
	UPDATE_KEY(pHGE, kInput.up,		HGEK_UP);
	UPDATE_KEY(pHGE, kInput.down,	HGEK_DOWN);
	UPDATE_KEY(pHGE, kInput.left,	HGEK_LEFT);
	UPDATE_KEY(pHGE, kInput.right,	HGEK_RIGHT);
	UPDATE_KEY(pHGE, kInput.debug,	HGEK_F11);
	UPDATE_KEY(pHGE, kInput.action,	HGEK_SPACE);

	if (kInput.debug == 1) {
		g_bDebug = !g_bDebug;
		pHGE->System_SetState(HGE_TITLE, g_bDebug ? "jsmn project [debug]" : "jsmn project");
	}
	
	if (pHGE->Input_GetKeyState(HGEK_ESCAPE))
		return true;

	switch (eState) {
		case STATE_MAIN:		UpdateMain(kInput);		break;
		case STATE_MAP_SWITCH:	UpdateMapSwitch();		break;
		case STATE_DIALOG:		UpdateDialog(kInput);	break;
        case STATE_MENU:        UpdateMenu(kInput);     break;
	}

	return false;
}

bool RenderFunc()
{
	pHGE->Gfx_BeginScene();
	pHGE->Gfx_Clear(0xFF000000);

	switch (eState) {
		case STATE_MAIN:		RenderMain();		break;
		case STATE_MAP_SWITCH:	RenderMapSwitch();	break;
		case STATE_DIALOG:		RenderDialog();		break;
        case STATE_MENU:        RenderMenu();       break;
	}
	
	pHGE->Gfx_EndScene();

	return false;
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
	pHGE = hgeCreate(HGE_VERSION);
	
	pHGE->System_SetState(HGE_FRAMEFUNC,	FrameFunc);
	pHGE->System_SetState(HGE_RENDERFUNC,	RenderFunc);
	pHGE->System_SetState(HGE_SCREENWIDTH,	CX_VIEW);
	pHGE->System_SetState(HGE_SCREENHEIGHT,	CY_VIEW);
	pHGE->System_SetState(HGE_SHOWSPLASH,	false);
	pHGE->System_SetState(HGE_WINDOWED,		true);
	pHGE->System_SetState(HGE_HIDEMOUSE,	false);
	pHGE->System_SetState(HGE_USESOUND,		false);
	pHGE->System_SetState(HGE_TITLE,		"jsmn project");
	pHGE->System_SetState(HGE_LOGFILE,		"game.log");
	pHGE->System_SetState(HGE_FPS,			60);

	if (pHGE->System_Initiate()) {
		if (LoadRes()) {
			pHGE->System_Start();
		} else {
			pHGE->System_Log("LoadRes failed.");
		}
	}

	FreeRes();
	pHGE->System_Shutdown();
	pHGE->Release();

	return 0;
}

